mod capnp_srv;
mod rpc_cli;
mod rpc_server;

use compio::buf::IoBuf;
use crate::rpc_cli::Command::{Add, Get, Remove, UpdatePrice, UpdateQuantity, Watch};
use crate::rpc_cli::{add, get, remove, update_price, update_quantity, watch, Options, FOO, AddOptions, GetOptions};
use crate::rpc_server::StoreInventory;
use grpc_gen::inventory_client::InventoryClient;
use grpc_gen::inventory_server::InventoryServer;
use ntex::web;
use parking_lot::Mutex;
use tonic_reflection::proto::FILE_DESCRIPTOR_SET;

#[web::get("/")]
async fn hello() -> impl web::Responder {
    web::HttpResponse::Ok().body("Hello world!")
}

#[web::post("/echo")]
async fn echo(req_body: String) -> impl web::Responder {
    web::HttpResponse::Ok().body(req_body)
}

async fn handle_opts(opts: Options) -> Result<String, Box<dyn std::error::Error>> {
    match opts.command {
        Add(opts) => add(opts).await,
        Remove(opts) => remove(opts).await,
        Get(opts) => get(opts).await,
        UpdateQuantity(opts) => update_quantity(opts).await,
        UpdatePrice(opts) => update_price(opts).await,
        Watch(opts) => watch(opts).await,
    }
}

#[web::get("/test-add-rpc")]
async fn test_add_rpc() -> impl web::Responder {
    let json = r#"
    {   
    "sku": "kkk",
    "price": 1.0,
    "quantity": 1
    }
    "#;
    let opts: AddOptions = sonic_rs::from_slice(json.as_slice()).unwrap();
    match add(opts).await {
        Ok(res) =>
            web::HttpResponse::Ok().body(res),
        Err(err) => 
            web::HttpResponse::Ok().body(err.to_string())
    }
}
#[web::get("/test-get-rpc")]
async fn test_get_rpc() -> impl web::Responder {
    let json = r#"
    {   
    "sku": "kkk"
    }
    "#;
    let opts: GetOptions = sonic_rs::from_slice(json.as_slice()).unwrap();
    match get(opts).await {
        Ok(res) =>
            web::HttpResponse::Ok().body(res),
        Err(err) =>
            web::HttpResponse::Ok().body(err.to_string())
    }
}

#[web::post("/rpc")]
async fn echo_rpc(req_body: String) -> impl web::Responder {
    let opts: Options = sonic_rs::from_str(&req_body).unwrap();
    match handle_opts(opts).await {
        Ok(res) => web::HttpResponse::Ok().body(res),
        Err(err) => web::HttpResponse::Ok().body(err.to_string()),
    }
}

async fn manual_hello() -> impl web::Responder {
    web::HttpResponse::Ok().body("hello world")
}

#[ntex::main]
async fn start_ntex() -> std::io::Result<()> {
    use compio::runtime::spawn;
    spawn(async {}).detach();
    web::HttpServer::new(|| {
        web::App::new()
            .service(hello)
            .service(echo)
            .service(echo_rpc)
            .service(test_add_rpc)
            .service(test_get_rpc)
            .route("/hey", web::get().to(manual_hello))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

#[tokio::main]
async fn start_rpc_server() -> std::io::Result<()> {
    let addr = "127.0.0.1:9001".parse().unwrap();
    let inventory = StoreInventory::default();

    let reflection_service = tonic_reflection::server::Builder::configure()
        .register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
        .build()
        .unwrap();
    let h = tokio::spawn(async {
        let client = InventoryClient::connect("http://127.0.0.1:9001")
            .await
            .unwrap();
        FOO.get_or_init(move || Mutex::new(client));
        println!("client init success");
    });
    tonic::transport::Server::builder()
        .add_service(InventoryServer::new(inventory))
        .add_service(reflection_service)
        .serve(addr)
        .await
        .unwrap();
    h.await?;
    Ok(())
}

fn main() -> std::io::Result<()> {
    let h1 = std::thread::spawn(move || start_ntex());
    let h2 = std::thread::spawn(move || start_rpc_server());
    h1.join().unwrap()?;
    h2.join().unwrap()?;
    Ok(())
}
